package com.bclz.propertytesting

import com.bclz.state.{RNG, Simple, State}

case class Gen[A](sample:State[RNG,A]){

  def flatMap[B](f:A=>Gen[B]):Gen[B]=
    Gen(this.sample.flatMap(a=>f(a).sample))

  def map[B](f:A=>B):Gen[B]=
    Gen(this.sample.map(f))


  def listOfN(gen: Gen[Int]):Gen[List[A]]=
    gen flatMap(n=>Gen.listOfN(n,this))

  def union(gen:Gen[A]):Gen[A]=
    Gen.boolean flatMap(b=>if(b) this else gen)




}

object Gen {
  def listOfN[A](n:Int,gen: Gen[A]):Gen[List[A]]=
    Gen(State.sequence(List.fill(n)(gen.sample)))

  def choose(start:Int,stopExclusive:Int):Gen[Int]=
    Gen( State[RNG,Int](r=>r.nextInt) map (i=>start+(i%(stopExclusive-start))))

  def unit[A](a: =>A):Gen[A]=
    Gen( State(s=>RNG.unit(a)(s)) )

  def boolean:Gen[Boolean]=
    Gen(State[RNG,Int](r=>r.nextInt) map(i => (i&1)==0))

  def weighted[A](g1:(Gen[A],Double),g2:(Gen[A],Double)):Gen[A]= g1 match {

    case (g,w)=> if(w>g2._2) g else if(w<g2._2) g2._1 else g.union(g2._1)

  }


  def main(args: Array[String]): Unit = {

    val s = Gen.listOfN(3,Gen.unit(6)).sample
    println(s.run(Simple(10L))._1)
    println(boolean.sample.run(Simple(10L))._1)

  }
}

trait Prop{

  def &&(p:Prop) :Prop=new Prop {
    override def check: Boolean = this.check && p.check
  }

  def check:Boolean
}
object Prop{

  def forAll[A](gen: Gen[A])(f:A=>Boolean):Prop = ???

}

